/**
 * Copyright (c) 2013-2019 Nikita Koksharov
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.redisson;

import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Semaphore;

import org.redisson.misc.RPromise;

/**
 * RedissonLockEntry，用作锁订阅的抽象。相同的订阅通道对应相同的RedissonLockEntry。
 * 因此同一个RedissonLockEntry可能会记录多个线程(因为他们订阅相同的通道)
 *
 * 以Lock的 EntryName 作为RedissonLockEntry的唯一标识
 */
public class RedissonLockEntry implements PubSubEntry<RedissonLockEntry> {

    /**
     * 该RedissonLockEntry记录了多少个线程。这些线程由于订阅相同的通道，于是公用这个RedissonLockEntry
     */
    private int counter;
    /**
     * JUC框架的Semaphore。正是通过该信号量来实现获取锁失败线程争抢线程的动作。
     * 因为一个RedissonLockEntry可能会有多个线程订阅，这些线程一起等待从latch中获取信号量。
     * 当redis发送解锁信息时，redisson将对应的RedissonLockEntry.latch上release一个信号量，上面那些线程立刻抢，谁抢到了，谁就是下一个加锁线程。
     * (公平锁?公平锁一个RedissonLockEntry只会被一个线程订阅，还抢啥抢)
     */
    private final Semaphore latch;
    /**
     * RPromise<RedissonLockEntry>。实际的实现类为 RedissonPromise<RedissonLockEntry>.
     * 这是一个CompletableFuture<T>的继承类，因此该属性可以进行异步任务编排。
     *
     * 该属性是针对RedissonLockEntry本身而言的。
     */
    private final RPromise<RedissonLockEntry> promise;
    /**
     * 保存所有公用当前RedissonLockEntry的线程。
     * 当线程等待锁而订阅通道时，是在监听通道中的锁释放消息，因此给该属性起名为listeners。
     *
     * 订阅通道的线程会制作成Runnable任务添加到对应通道的RedissonLockEntry中(详见PublishSubscribe类)。
     * 当通道发布锁释放消息时，从listeners中poll出一个线程来获取锁(详见LockPubSub类)。
     */
    private final ConcurrentLinkedQueue<Runnable> listeners = new ConcurrentLinkedQueue<Runnable>();

    /**
     * 注意初始化RedissonLockEntry时，latch的值为0. latch值的增加只会在'redis发送解锁通知时增加'。
     * @param promise
     */
    public RedissonLockEntry(RPromise<RedissonLockEntry> promise) {
        super();
        this.latch = new Semaphore(0);
        this.promise = promise;
    }

    /**
     * 核心。获取RedissonLockEntry内的信号量锁。
     * 通过执行 RedissonLockEntry.latch.tryAcquire(ttl)来在ttl时间内阻塞。直到ttl过期或者在此期间latch被释放了一个信号量并且抢到。
     * @return
     */
    public Semaphore getLatch() {
        return latch;
    }

    /**
     * 有一个线程尝试加入到该RedissonLockEntry(即该通道)。
     * 于是将记录Entry中线程数量的指标++
     */
    public void acquire() {
        counter++;
    }

    public int release() {
        return --counter;
    }

    public RPromise<RedissonLockEntry> getPromise() {
        return promise;
    }

    /**
     * 将一个订阅通道的线程制作成的Runnable任务添加到该RedissonLockEntry(即该通道)。
     */
    public void addListener(Runnable listener) {
        listeners.add(listener);
    }

    /**
     * 将一个订阅通道的线程制作成的Runnable任务从该RedissonLockEntry(即该通道)移除。是取消订阅时的操作。
     */
    public boolean removeListener(Runnable listener) {
        return listeners.remove(listener);
    }

    public ConcurrentLinkedQueue<Runnable> getListeners() {
        return listeners;
    }

}
